#include "XMLParser.h"
#include <exception>

using std::cerr;
using std::cout;
using std::endl;
using std::ifstream;
using namespace std;



XMLParser::XMLParser(){}

XMLParser::~XMLParser(){}

bool XMLParser::OpenFile (char* filePath, ifstream* stream){
	
	(*stream).open(filePath);
	if(!*stream) { // file couldn't be opened
		  return false;
	 }

	return true;
}
	 

int XMLParser::XMLParse(char* filePath, list<MapList*>* xmlObjects){	
	ifstream stream;
	int error;
		
	if (OpenFile(filePath, &stream)){
		error = ReadXML(&stream, xmlObjects);		
	}	

	if(stream.is_open())
		stream.close();
	
	if (error == -1){
		throw ParsingExcep("Ha ocurrido un error en el parseo");
	}
	return error;		
}

int IsClosureTag(string line, int fromPosition) {		
	if ((line.find(">", fromPosition) == fromPosition) && (line.size() == fromPosition + 1)){
		return 0;
	}	

	if ((line.find("/>", fromPosition) == fromPosition) && (line.size() == fromPosition + 2)){
		return 1;
	}		

	if (line.size() == fromPosition){
		return -1;
	}


	return -2;
}

void ShowMeThePapa(list<MapList*> xmlObjects)
{
	/*	MapList *mapList;
	map<string, CustomizedMap> types;
	map<string, CustomizedMap>::iterator ii;
	map<string, string>::iterator jj;
	list<MapList*>::iterator it;
	int quantity;
	int quantity2;

	for (it= xmlObjects.begin() ; it!=xmlObjects.end(); it++)
	{	
		cout << endl;
		mapList = *it;
		cout<<mapList->getType()<< endl;
		types = mapList->mapList;
		
		quantity = mapList->getElementQuantity();

//		CustomizedMap *mapa = mapList->getElement("circulo");
//			for( jj=mapa->customMap.begin(); jj!=mapa->customMap.end(); ++jj) {
//				cout << (*jj).first << " ";
//				cout << (*jj).second << " ";
//			}


    	for(ii=types.begin(); ii!=types.end(); ++ii){
		   cout << (*ii).first << " " << quantity << " ";
		   CustomizedMap map = ((CustomizedMap)(*ii).second);

		   quantity2 = map.getAttributeQuantity();
		   for( jj=map.customMap.begin(); jj!=map.customMap.end(); ++jj) {
				cout << (*jj).first << " ";
				cout << (*jj).second << " " << quantity2 << " ";
			}
		   cout << endl;
		}
	}*/
}

void isAtributeDuplicated(CustomizedMap custom, string key){
	/*map<string, string>::iterator jj;
	
	for( jj=custom.customMap.begin(); jj!=custom.customMap.end(); ++jj) {
		if ((*jj).first == key){
			throw ParsingExcep("Se ha insertado la siguiente clave repetida: " + key);			
		}
	}*/	
}

void isAtributeTypeDuplicated(map<string, CustomizedMap> custom, string key){
	/*map<string, CustomizedMap>::iterator jj;
	
	for( jj=custom.begin(); jj!=custom.end(); ++jj) {
		if ((*jj).first == key){
			throw ParsingExcep("Se ha insertado la siguiente clave repetida: " + key);
		}
	}*/
}

int XMLParser::ParseKeyAndValue(string line){

	char quote = 34;
	char equal = '=';	

	int keyPosition = line.find(equal, 0);
	string key = line.substr(0, keyPosition);
	if (key[key.size()-1] == ' '){
		key = key.substr(0,key.size()-1);
	}
	//isAtributeDuplicated(attributes.customMap, key)	;		
			
	int firstQuotePosition = line.find(quote, 0);
	int secondQuotePosition = line.find(quote, firstQuotePosition + 1);

	if ((firstQuotePosition != -1) && (secondQuotePosition != -1)){

		string value = line.substr(firstQuotePosition + 1, secondQuotePosition -1 - firstQuotePosition);
		attributes.customMap.insert(pair<string, string>(key, value));
		
		switch(IsClosureTag(line, secondQuotePosition +1)){
		case -2: throw ParsingExcep("Se esperaba un cierre de tag");
				 break;

		case -1: return -1;
				 break;

		case 0: return 0;
				break;

		case 1: return 1;
				break;
		}														
	}
	else{
			switch(IsClosureTag(line, 0)){
			case -2: throw ParsingExcep("Se esperaba un cierre de tag");
					 break;

			case -1: throw ParsingExcep("Se esperaba un cierre de tag");
					 break;

			case 0: return 0;
					break;

			case 1: return 1;
					break;
			}

		}
}



int XMLParser::ReadXML(ifstream* stream, list<MapList*>* xmlObjects)
{
	bool isOpenTag = true;
	int openTagCounter = 0;
	string type;
	char quote = 34;
	char equal = '=';		
	bool firstTagOpen = false;
	string objectType = "";
	bool isObjectType = true;
	bool needCloseTag = false;
	bool keepAddingToLine = false;
	string addedLine;
	bool equalFound = false;
	bool firstQuoteFound = false;
	bool secondQuoteFound = false;
	bool isGeneralType = false;
	bool isFirstEntryToGeneral = true;
	string atributeType = "";
	int firstQuotePosition;	
	int reedNewLine = true;
	string line;
	bool tagScenarioSpected = true;
	bool tagScenarioClosureFound = false;
	string closeTag = "";
	string escenario = "Escenario";
	string general = "General";

	while (!(*stream).eof()){      
	  if (reedNewLine){	
		(*stream) >> line;
	  }		
	  reedNewLine = true;

	  if((tagScenarioClosureFound) && (!needCloseTag)){
		throw ParsingExcep("Se ha cerrado el tag principal y el archivo sigue conteniendo informacion");
	  }	  

	  if (keepAddingToLine){
			addedLine = addedLine + " " + line;
			keepAddingToLine = false;

			line = addedLine;
	  }	  

	  if (!tagScenarioSpected){

		  if (isOpenTag){
			  if (needCloseTag){
				  if(line != ">"){
					 throw ParsingExcep("Se esperaba '>'");
				  }
				  needCloseTag = false;
			  }
			  else{
				  if (line != "<"){
					  if(!firstTagOpen){
						  if ((isGeneralType) && (line.find(general, 0) == -1) && (line[0] != '<')){
								string key = type;
								int pos = line.find(quote, 0);
								string value;
								if(pos != -1){
									line.replace(pos, 1, "");
									pos = line.find(quote, 0);
									if (pos != -1){
										line.replace(pos, 1, "");
									}
								}
								
								pos = line.find('<', 0);
								if (pos != -1){
									value = line.substr(0, pos);
									line = line.substr(pos -1, line.size() -1);								
								}
								else{
									value = line;								
								}
								attributes.customMap.insert(pair<string, string>(key, value));
								atributeType = key;
								openTagCounter++;
						  }
						  else if (line[0] != '<'){
								throw ParsingExcep("Se esperaba '<'");
						  }
							
						  type = line.replace(0, 1, "");
						  int pos = line.find('>', 0);
						  
						  if ((pos == -1) || (line.size() == pos +1 )){					   
							type = line;
						  }
						  else{						  
							type = line.substr(0, pos);
							line = line.substr(pos +1 , line.size() -1);
							reedNewLine = false;
							isOpenTag = true;
							firstTagOpen = false;						  
						  }					 
					  }
					  else{
						  int pos = line.find('>', 0);
						  if ((pos == -1) || (line.size() == pos +1 )){					   
							type = line;
						  }
						  else{						  
							type = line.substr(0, pos);
							line = line.substr(pos +1 , line.size() -1);
							reedNewLine = false;
							isOpenTag = true;
							firstTagOpen = false;						  
						  }

					  }		
					  if((line[line.size()-1] == '>') && (reedNewLine)){
							type = type.replace(line.size() - 1, 1, "");
							isOpenTag = true;
							firstTagOpen = false;
					  }
					  if(isObjectType){
						  isObjectType = false;
						  objectType = type;						  
					  }	
					  
					  if(line == "/" + objectType + ">")
					  {						  
						  MapList *mapList = new MapList();
						  mapList->setType(objectType);
						  
						  mapList->mapList = types.mapList;
						  (*xmlObjects).push_back(mapList);
						  types.mapList.clear();
						  isObjectType = true;	
						  if (objectType == general){								
								objectType = "";
						  }
					  }
					  else if(line == "/" + objectType){						  
						  MapList *mapList = new MapList();
						  mapList->setType(objectType);
						  mapList->mapList = types.mapList;
						  (*xmlObjects).push_back(mapList);
						  types.mapList.clear();
						  isObjectType = true;
						  needCloseTag = true;
						  if (objectType == general){								
								objectType = "";
						  }
					  }
					  else if(line == "/" + escenario + ">"){
						tagScenarioClosureFound = true;
					  }	
					  else if(line == "/" + escenario){
						tagScenarioClosureFound = true;
						needCloseTag = true;
					  }
					  else
					  {			
						  if((line[line.size()-1] != '>') && (reedNewLine)){
								isOpenTag = false;								
								if (!isGeneralType){
									openTagCounter++;
								}
						  }
						  else if ((line == objectType + ">") && (line !=general + ">")){																
								if (!isGeneralType){
									openTagCounter++;
									isOpenTag = true;
								}

						  }
					  }				  
				  }
				  else{
					  firstTagOpen = true;	  
				  }
			  }		   		  

			  if (objectType == general){

				  if ((type == general) && (!isFirstEntryToGeneral) && (!firstTagOpen)){
					throw ParsingExcep("Solo puede haber un tag 'General'");
				  }
				isGeneralType = true;			
				if ((line.find('>', line.size() - 2) == -1) && (isFirstEntryToGeneral)){
					needCloseTag = true;				
				}
				if(isFirstEntryToGeneral){
					openTagCounter++;
				}
			
				isFirstEntryToGeneral = false;
			  }
			  else{
				isGeneralType = false;	
			  }

		  }
		  else{	
			  
			  switch(IsClosureTag(line, 0)){	

				case 0: equalFound = true;
						firstQuoteFound = true;
						secondQuoteFound = true;
						break;

				case 1: equalFound = true;
						firstQuoteFound = true;
						secondQuoteFound = true;
						break;
			  }		  

			  if(!equalFound){
				int keyPosition = line.find(equal, 0);
				if(keyPosition == -1){
					keepAddingToLine = true;
					addedLine = line;
					equalFound = false;
				}
				else{
					equalFound = true;
				}
			  }

			  if (!firstQuoteFound){
				firstQuotePosition = line.find(quote, 0);
				if(firstQuotePosition == -1){
					keepAddingToLine = true;
					addedLine = line;
					firstQuoteFound = false;
				}
				else{
					firstQuoteFound = true;
				}
			  }

			  if (firstQuoteFound){
				  if (!secondQuoteFound){
					int secondQuotePosition = line.find(quote, firstQuotePosition +1);
					if(secondQuotePosition == -1){
						keepAddingToLine = true;
						addedLine = line;
						secondQuoteFound = false;
					}
					else{
						secondQuoteFound = true;
					}
				  }
			  }

			  if((line[0] == '>') && (line.size() > 1)){
					line = line.substr(1, line.size() -1);
					reedNewLine = false;
					isOpenTag = true;
					firstTagOpen = false;
					keepAddingToLine = false;
			  }
			  
			  else if ((equalFound) && (firstQuoteFound) && (secondQuoteFound)){			    
					int KeyAndValue = ParseKeyAndValue(line);	  

					switch (KeyAndValue){
						case -2: return -1;
								 break;	

						case -1: equalFound = false;
								 firstQuoteFound = false;
								 secondQuoteFound = false;
								 break;

						case 0: isOpenTag = true;
								firstTagOpen = false;
								if(!isGeneralType){
								//	isAtributeTypeDuplicated(types.mapList, type);					
									types.mapList.insert(pair<string, CustomizedMap>(type, attributes));
									attributes.customMap.clear();
								}
								equalFound = false;
								firstQuoteFound = false;
								secondQuoteFound = false;
								break;

						case 1: isOpenTag = true;
								firstTagOpen = false;
								if(!isGeneralType){
								//	isAtributeTypeDuplicated(types.mapList, type);					
									types.mapList.insert(pair<string, CustomizedMap>(type, attributes));
									attributes.customMap.clear();										
								}
								openTagCounter--;
								equalFound = false;
								firstQuoteFound = false;
								secondQuoteFound = false;
								break;
					}				
			  }									
			}
		  
			if (type != ""){
				closeTag = type.substr(1, type.size()-1); 
			}
		  
		  if(line == "</" + atributeType + ">")
		  {
			  openTagCounter--;
			//  isAtributeTypeDuplicated(types.mapList, atributeType);
			  types.mapList.insert(pair<string, CustomizedMap>(atributeType, attributes));
			  attributes.customMap.clear();
			  equalFound = false;
			  firstQuoteFound = false;
			  secondQuoteFound = false;				
			  isOpenTag = true;
			  keepAddingToLine = false;
		  }
		  else if(line == "</" + atributeType){
			  openTagCounter--;
			  //isAtributeTypeDuplicated(types.mapList, atributeType);
			  types.mapList.insert(pair<string, CustomizedMap>(atributeType, attributes));
			  attributes.customMap.clear();
			  equalFound = false;
			  firstQuoteFound = false;
			  secondQuoteFound = false;
			  needCloseTag = true;
			  isOpenTag = true;
			  keepAddingToLine = false;
		  }
		  else if(line == "/" + closeTag + ">"){
			  openTagCounter--;
			  equalFound = false;
			  firstQuoteFound = false;
			  secondQuoteFound = false;				
			  isOpenTag = true;
			  keepAddingToLine = false;
		  }

		  else if(line == "/" + closeTag){
			  openTagCounter--;
			  equalFound = false;
			  firstQuoteFound = false;
			  secondQuoteFound = false;				
			  isOpenTag = true;
			  keepAddingToLine = false;
			  needCloseTag = true;
		  }
		}
		else{
			if((line == "<" + escenario + ">") || (line == "<" + escenario + " >") || (line == "< " + escenario + " >") || (line == "< " + escenario + ">")){
				tagScenarioSpected = false;
				openTagCounter++;
			}
			else{
				keepAddingToLine = true;
				addedLine = line;
			}			
		}
	}
	if(openTagCounter != 0){
		throw ParsingExcep("Hay un error de parseo en el archivo");
	}
	if (tagScenarioClosureFound){
		ShowMeThePapa(*xmlObjects);	
		return 1;
	}
	else{
		throw ParsingExcep("Se ha terminado de leer el archivo y ne se ha encontrado el cierre del tag principal <Escenario>");
	}
}





